/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package technotheist.geo.map.vector;

import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import technotheist.geo.map.MapUV;
import technotheist.geo.map.MapUtil;
import technotheist.geo.map.VectorMap;
import technotheist.geo.map.scalar.FloatMap;

/**
 *
 * @author Mitchel Strachan
 */
public class Float3Map extends FloatMap implements VectorMap<Vector3f> {

    public final int layers;
    public final int vecSize;

    public Float3Map(int sideLength) {
        this(sideLength, false);
    }

    public Float3Map(int sideLength, boolean stride4) {
        this(sideLength, 1, stride4);
    }

    public Float3Map(int sideLength, int layers) {
        this(sideLength, layers, false);
    }

    public Float3Map(int sideLength, int layers, boolean stride4) {
        super(sideLength, layers * (stride4 ? 4 : 3));
        this.layers = layers;
        this.vecSize = stride4 ? 4 : 3;
    }

    @Override
    public Vector3f get(int p, int u, int v, Vector3f store) {
        return get(p, u, v, 0, store);
    }

    @Override
    public Vector3f get(int p, int u, int v, int layer, Vector3f store) {
        int i = index(p, u, v, layer);
        Vector3f vec = store == null ? new Vector3f() : store;
        vec.x = getFloat(i++);
        vec.y = getFloat(i++);
        vec.z = getFloat(i);
        return vec;
    }

    @Override
    public void set(int p, int u, int v, Vector3f value) {
        set(p, u, v, 0, value);
    }

    @Override
    public void set(int p, int u, int v, int layer, Vector3f value) {
        int i = index(p, u, v, layer);
        setFloat(i++, value.x);
        setFloat(i++, value.y);
        setFloat(i, value.z);
    }

    @Override
    public Vector3f getNor(int p, float u, float v, Vector3f store) {
        return getNor(p, u, v, 0, store);
    }

    @Override
    public Vector3f getNor(int p, float u, float v, int layer, Vector3f store) {
        return get(p, u * (sideLength + 1), v * (sideLength + 1), 0, store);
    }

    @Override
    public Vector3f get(int p, float u, float v, Vector3f store) {
        return get(p, u, v, 0, store);
    }

    @Override
    public Vector3f get(int p, float u, float v, int layer, Vector3f store) {
        Vector3f vec = store == null ? new Vector3f() : store;
        int u0 = (int) FastMath.floor(u);
        int v0 = (int) FastMath.floor(v);

        float du = u - u0;
        float dv = v - v0;

        int i00 = index(p, u0, v0, layer);
        int i10 = index(p, u0, v0, layer);
        int i01 = index(p, u0, v0, layer);
        int i11 = index(p, u0, v0, layer);

        float d00 = (1 - du) * (1 - dv);
        float d10 = du * (1 - dv);
        float d01 = (1 - du) * dv;
        float d11 = du * dv;

        vec.x = getFloat(i00) * d00
                + getFloat(i10) * d10
                + getFloat(i01) * d01
                + getFloat(i11) * d11;
        vec.y = getFloat(i00 + 1) * d00
                + getFloat(i10 + 1) * d10
                + getFloat(i01 + 1) * d01
                + getFloat(i11 + 1) * d11;
        vec.z = getFloat(i00 + 2) * d00
                + getFloat(i10 + 2) * d10
                + getFloat(i01 + 2) * d01
                + getFloat(i11 + 2) * d11;
        return vec;
    }

    public Vector3f get(int index, Vector3f store) {
        return get(index, 0, store);
    }

    public void set(int index, Vector3f value) {
        set(index, 0, value);
    }

    public Vector3f get(int index, int layer, Vector3f store) {
        int i = index * components + layer * vecSize;
        Vector3f vec = store == null ? new Vector3f() : store;
        vec.x = getFloat(i++);
        vec.y = getFloat(i++);
        vec.z = getFloat(i++);
        return vec;
    }

    public void set(int index, int layer, Vector3f value) {
        int i = index * components + layer * vecSize;
        setFloat(i++, value.x);
        setFloat(i++, value.y);
        setFloat(i++, value.z);
//        setFloat(i++, value.w);
    }

    public int index(int p, int u, int v, int layer) {
        if (layer < 0 || layer >= layers) {
            throw new IllegalArgumentException("layer must be between 0 and " + layers);
        }
        return index(p, u, v) + layer * vecSize;
    }
    
    

    @Override
    public Vector3f get(MapUV mp, Vector3f store) {
        return getNor(mp.panel(), mp.u, mp.v, store);
    }

    @Override
    public Vector3f get(MapUV mp, int layer, Vector3f store) {
        return getNor(mp.panel(), mp.u, mp.v, layer, store);
    }
    
}
